package com.maple.webrtc.socket

import android.content.Context
import android.net.http.UrlRequest.StatusListener
import android.os.Build
import android.se.omapi.SEService.OnConnectedListener
import com.blankj.utilcode.util.GsonUtils
import com.maple.webrtc.utils.LogUtils
import io.ktor.client.plugins.HttpTimeout
import io.ktor.server.application.install
import io.ktor.server.engine.ApplicationEngine
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import io.ktor.server.netty.NettyApplicationEngine
import io.ktor.server.plugins.origin
import io.ktor.server.routing.Routing
import io.ktor.server.routing.routing
import io.ktor.server.websocket.WebSockets
import io.ktor.server.websocket.pingPeriod
import io.ktor.server.websocket.timeout
import io.ktor.server.websocket.webSocket
import io.ktor.websocket.CloseReason
import io.ktor.websocket.Frame
import io.ktor.websocket.WebSocketSession
import io.ktor.websocket.close
import io.ktor.websocket.readText
import io.ktor.websocket.send
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
import java.time.Duration
import java.util.concurrent.ConcurrentHashMap


class Server(private val context: Context ,private val port: Int) {
    private var server: ApplicationEngine? = null

    private val connectedClients = ConcurrentHashMap<String, WebSocketSession>()

    private var callback: Callback? = null

    fun setCallback(callback: Callback?) {
        this.callback = callback
    }


    fun start() {
        server = embeddedServer(Netty, host = "0.0.0.0", port = port) {
            install(WebSockets) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    pingPeriod = Duration.ofSeconds(15)
                    timeout = Duration.ofSeconds(15)
                }
                maxFrameSize = Long.MAX_VALUE
                masking = false
            }
            callback?.onListen("${port}")
            routing {
                webSocket("/socket") {
                    val clientAddress = call.request.origin.remoteAddress
                    connectedClients[clientAddress] = this
                    withContext(Dispatchers.Main) {
                        callback?.onClientConnected(clientAddress)
                    }
                    try {
                        incoming.consumeEach { frame ->
                            if (frame is Frame.Text) {
                                val receivedText = frame.readText()
                                withContext(Dispatchers.Main) {
                                    callback?.onReceive(JSONObject(receivedText))
                                }
                            }
                        }
                    } catch (e: Exception) {
                        LogUtils.logGGQ("Server start ERROR:${e.message}")
                    } finally {
                        connectedClients.remove(clientAddress)
                    }
                }
            }
        }.start(wait = false)
    }


    fun send(json: JSONObject) {
        CoroutineScope(Dispatchers.IO).launch {
            try {
                val user = json.getString("user")
                connectedClients.get(user)?.send(json.toString())
            } catch (e: Exception) {
                LogUtils.logGGQ("Client send ERROR：${e.message}")
            }
        }
    }

    fun stop() {
        server?.stop(1000, 10000)
        connectedClients.clear()
    }


    interface Callback {
        fun onListen(port: String)
        fun onClientConnected(host: String)
        fun onReceive(json: JSONObject)
    }
}
